package com.twang.stock.config.redisLock;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Service
public class LuaScriptService {

    public static void main(String[] args) {
        //        248fc94ee62fbca173c05b4978ecf4d968a8f308 -- getLockNxpx
        //       ad1f7bea905538885f1608ebcc8bdccb05bf069d -- getValueCache
        //       7a5d3603081f0bca838c76e7abd18b05201227fe -- checkHas
        // 959a8c2144d8ea0a738daf6d9baec2aee66e2973 -- getValueByIncrCache

        // 65b7740454934d1f25d101cd0ee4be54742de202 -- getValueByIncrAndExpireCache
        // 2c0f9e6b209c911131e5ae7f38b9bdd0721e90bf -- getLockSetCache
        String luaScript=""
                +"\r\nlocal val = redis.call('GET', KEYS[1]);"
                +"\r\nlocal ret= 0;"
                +"\r\nif val == ARGV[1] then"
                +"\r\nret =redis.call('DEL',KEYS[1]);"
                +"\r\nend"
                +"\r\nreturn ret";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        System.out.println(redisScript.getSha1());
    }

    @Resource
    private RedisTemplate redisTemplate;

    public Object checkHas(String key, String value){
        String luaScript=""
                +"\r\nlocal key = '"+key+"';"
                +"\r\nlocal haskey = redis.call('EXISTS', key)"
                +"\r\nif haskey == 0 then"
                +"\r\n local ret = redis.call('SET', key,'" + value + "');"
                +"\r\nend"
                +"\r\nreturn haskey";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object checkHasCache(String key, String value){
        String luaScript=""
                +"\r\nlocal key = KEYS[1];"
                +"\r\nlocal haskey = redis.call('EXISTS', key)"
                +"\r\nif haskey == 0 then"
                +"\r\n local ret = redis.call('SET', key,  ARGV[1]);"
                +"\r\nend"
                +"\r\nreturn haskey";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return redisTemplate.execute(redisScript, keys, value);
    }

    public Object getValue(Integer step, String miliSecondKey, Integer startStep ){
        String luaScript=""
                +"\r\nlocal step = "+step+";"//步长
                +"\r\nlocal key = '"+miliSecondKey+"';"
                +"\r\nlocal count;"
                +"\r\nrepeat"
                +"\r\n  count = tonumber(redis.call('INCRBY', key, step));"
                +"\r\nuntil count < (1024 - step)"
                +"\r\nif count == step then"
                +"\r\n  redis.call('PEXPIRE', key, 1);"
                +"\r\nend"
                +"\r\nlocal now = redis.call('TIME');"
                +"\r\nreturn {tonumber(now[1]), tonumber(now[2]), count + "+startStep+"}";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(luaScript, List.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object getValueCache(Integer step, String miliSecondKey, Integer startStep ){
        String luaScript=""
                +"\r\nlocal step = tonumber(ARGV[1]);"//步长
                +"\r\nlocal key = KEYS[1];"
                +"\r\nlocal startStep = tonumber(ARGV[2]);"
                +"\r\nlocal count;"
                +"\r\nrepeat"
                +"\r\n  count = tonumber(redis.call('INCRBY', key, step));"
                +"\r\nuntil count < (1024 - step)"
                +"\r\nif count == step then"
                +"\r\n  redis.call('PEXPIRE', key, 1);"
                +"\r\nend"
                +"\r\nlocal now = redis.call('TIME');"
                +"\r\nreturn {tonumber(now[1]), tonumber(now[2]), count + startStep}";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(luaScript, List.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(miliSecondKey);
        return redisTemplate.execute(redisScript, keys, step, startStep);
    }


    public Object setValueCache(String key, String oldKey, String userId){
        String luaScript=
                "local timeVal = redis.call('TTL', KEYS[2]);"
                +"\r\nredis.call('SET', KEYS[1], KEYS[3]);"
                +"\r\nredis.call('EXPIRE', KEYS[1], timeVal);"
                +"return timeVal";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        keys.add(oldKey);
        keys.add(userId);
        return redisTemplate.execute(redisScript, keys);
    }

    public Object getValueByIncr(String key){
        int maxMantissa=4194304;//2的22次方
        String luaScript=""
                +"\r\nlocal count = tonumber(redis.call('INCR', '"+key+"'));"
                +"\r\nif count >= "+(maxMantissa-1)+" then"
                +"\r\n  redis.call('DEL', '"+key+"');"
                +"\r\nend"
                +"\r\nlocal now = redis.call('TIME');"
                +"\r\nreturn {tonumber(now[1]), tonumber(now[2]), count}";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(luaScript,List.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object getValueByIncrCache(String key){
        int maxMantissa=4194303;//2的22次方
        String luaScript=""
                +"\r\nlocal key = KEYS[1];"
                +"\r\nlocal maxMantissa = tonumber(ARGV[1]);"
                +"\r\nlocal count = tonumber(redis.call('INCR', key));"
                +"\r\nif count >= maxMantissa then"
                +"\r\n  redis.call('DEL', key);"
                +"\r\nend"
                +"\r\nlocal now = redis.call('TIME');"
                +"\r\nreturn {tonumber(now[1]), tonumber(now[2]), count}";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(luaScript,List.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return redisTemplate.execute(redisScript, keys, maxMantissa);
    }

    public Object getValueByIncrAndExpire(String key, int expire){
        String luaScript=""
                +"\r\nlocal count = tonumber(redis.call('INCR', '"+key+"'));"
                +"\r\nredis.call('EXPIRE', '"+key+"', "+expire+");"
                +"\r\nreturn count";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object getValueByIncrAndExpireCache(String key, int expire){
        String luaScript=""
                +"\r\nlocal expire = tonumber(ARGV[1]);"
                +"\r\nlocal count = tonumber(redis.call('INCR', KEYS[1]));"
                +"\r\nredis.call('EXPIRE', KEYS[1], expire);"
                +"\r\nreturn count";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return redisTemplate.execute(redisScript, keys, expire);
    }

    public Object getLockSet(String key, String value){
        String luaScript=""
                +"\r\nlocal val = redis.call('GET', '"+key+"');"
                +"\r\nlocal ret= 0;"
                +"\r\nif val == '"+value+"' then"
                +"\r\nret =redis.call('DEL','"+key+"');"
                +"\r\nend"
                +"\r\nreturn ret";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object getLockSetCache(String key, String value){
        String luaScript=""
                +"\r\nlocal val = redis.call('GET', KEYS[1]);"
                +"\r\nlocal ret= 0;"
                +"\r\nif val == ARGV[1] then"
                +"\r\nret =redis.call('DEL',KEYS[1]);"
                +"\r\nend"
                +"\r\nreturn ret";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return redisTemplate.execute(redisScript, keys, value);
    }

    public Object getLockNxpx(String key, String value, int millseconds){
        String luaScript=""
                +"\r\nlocal ret = tonumber(redis.call('SETNX', '"+key+"','"+value+"'));"
                +"\r\nredis.call('PEXPIRE','"+key+"','"+millseconds+"');"
                +"\r\nreturn ret";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        return redisTemplate.execute(redisScript, new ArrayList(), "");
    }

    public Object getLockNxpxCache(String key, String value, int millseconds){
        String luaScript=""
                +"\r\nlocal millseconds = tonumber(ARGV[2]);"
                +"\r\nlocal ret = tonumber(redis.call('SETNX', KEYS[1], ARGV[1]));"
                +"\r\nredis.call('PEXPIRE', KEYS[1], millseconds);"
                +"\r\nreturn ret";

        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<Long>(luaScript,Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        List<String> keys = new ArrayList<>();
        keys.add(key);
        return redisTemplate.execute(redisScript, keys, value, millseconds);
    }
}
